<?php
/**
 * @abstract
 * @author   Nam Nguyen <nam.ngvan@gmail.com>
 * @version  1.0
 * @since    Apr 17, 2013
 */
abstract class Nuo_Request_Abstract
{

	/**
	 * @var bool
	 */
	protected $_isExecuted = FALSE;

	/**
	 * @var method
	 */
	protected $_method = 'GET';

	/**
	 * @var string
	 */
	protected $_name = NULL;

	/**
	 * @var string
	 */
	protected $_path = NULL;

	/**
	 * @var array
	 */
	public $params = array();

	/**
	 * cosntructore
	 */
	public function __construct($name, $params = NULL)
	{
		$this -> setName($name);
		$this -> setPath($name);
		$this -> setParams($params);
	}

	/**
	 * get uri path of request
	 * /$index1/$index2
	 * @param  int  $index
	 * @return string/NULL
	 */
	public function getUriPart($index)
	{
		$parts = explode('/', $this -> _path);

		if (isset($parts[$index]))
		{
			return $parts[$index];
		}
		return NULL;
	}

	/**
	 * @return string
	 */
	public function __toString()
	{
		return (string)$this -> getCode();
	}

	public function isExecuted()
	{
		return $this -> _isExecuted;
	}

	/**
	 * @return string
	 */
	public function getPath()
	{
		return $this -> _path;
	}

	public function setPath($path)
	{
		$this -> _path = strtolower(trim($path, '/'));
		return $this;
	}

	/**
	 * @param string $key
	 * @param mixed $value NULL
	 * @return mixed
	 */
	public function getParam($key, $default = NULL)
	{
		return isset($this -> params[$key]) ? $this -> params[$key] : $default;
	}

	/**
	 * @return array
	 */
	public function getParams()
	{
		return $this -> params;
	}

	/**
	 * @param string $name
	 * @param any $value
	 * @return Request
	 */
	public function setParam($name, $value)
	{
		$this -> params[$name] = $value;
		return $this;
	}

	/**
	 * @param array $params
	 * @return Request
	 */
	public function setParams($params)
	{
		if ($params)
		{
			foreach ($params as $name => $value)
			{
				$this -> params[$name] = $value;
			}
		}

		return $this;
	}

	/**
	 * @param string $method
	 * @return Request
	 */
	public function setMethod($method)
	{
		$this -> _method = strtoupper($method);
		return $this;
	}

	/**
	 * @return string
	 */
	public function getMethod()
	{
		return $this -> _method;
	}

	/**
	 * @return bool
	 */
	public function isGet()
	{
		return 'GET' == $this -> _method;
	}

	/**
	 * @return bool
	 */
	public function isPost()
	{
		return 'POST' == $this -> _method;
	}

	/**
	 * @return bool
	 */
	public function isPut()
	{
		return 'PUT' == $this -> _method;
	}

	/**
	 * @return bool
	 */
	public function isDelete()
	{
		return 'DELETE' == $this -> _method;
	}

	/**
	 * @return bool
	 */
	public function isHead()
	{
		return 'HEAD' == $this -> _method;
	}

	/**
	 * @return string
	 */
	public function getName()
	{
		if (NULL == $this -> _name)
		{
			Nuo::getRouter() -> execute($this);
		}
		return $this -> _name;
	}

	/**
	 * @param string $name
	 * @return Request
	 */
	public function setName($name)
	{
		$this -> _name = $name;
		return $this;
	}

	/**
	 * dispatch a request
	 * @return Request
	 */
	public function execute()
	{

		if ($this -> _isExecuted)
		{
			return $this;
		}

		$this -> _isExecuted = TRUE;

		// format path.

		$name = $this -> getName();

		$class = inflect($name);
		
		if (!Nuo::loadModuleClass($class))
		{
			trigger_error('Could not load action class "' . $class . '" for action ' . $name, E_USER_ERROR);
		}
		else
		{
			$handler = new $class;

			$content = $handler -> execute($this);

			$this -> _isExecuted = TRUE;

			// manual unset action.
			unset($handler);

			Nuo::getResponse() -> setContent($content);
		}
	}

	/**
	 * for ward request to new action handler
	 * @param string $path
	 * @param string $text.
	 */
	public function forward($name)
	{
		$this -> setName($name) -> setPath($name);

		$class = inflect($name);

		if (!Nuo::loadModuleClass($class))
		{
			trigger_error('Could not load action class "' . $class . '" for action ' . $name, E_USER_ERROR);
		}
		else
		{
			$handler = new $class;

			$content = $handler -> execute($this);

			$this -> _isExecuted = TRUE;

			// manual unset action.
			unset($handler);
			return $content;
		}
	}


	/**
	 * @param string $name
	 * @param array $params
	 * @return Request
	 */
	static public function factory($name, $params = array())
	{
		return new self($name, $params);
	}

	/**
	 * Retrieve a member of the $_COOKIE superglobal
	 * If no $key is passed, returns the entire $_COOKIE array.
	 * @todo How to retrieve from nested arrays
	 * @static
	 * @access  public
	 * @param   string $key
	 * @param   mixed $default Default value to use if key not found
	 * @return  mixed Returns NULL if key does not exist
	 */
	static public function getCookie($key = NULL, $default = NULL)
	{
		return NULL === $key ? $_COOKIE : (isset($_COOKIE[$key]) ? $_COOKIE[$key] : $default);
	}

	/**
	 * @static
	 * @access  public
	 * @param  string $key
	 * @param  string $default
	 * @return string
	 */
	static public function getServer($key = NULL, $default = NULL)
	{
		return NULL === $key ? $_SERVER : (isset($_SERVER[$key])) ? $_SERVER[$key] : $default;
	}

	/**
	 * @static
	 * @access  public
	 * @param   string   $key
	 * @param   string   $default
	 * @return  string
	 */
	static public function getEnv($key = NULL, $default = NULL)
	{
		return NULL === $key ? $_ENV : (isset($_ENV[$key])) ? $_ENV[$key] : $default;
	}

	/**
	 * @static
	 * @access  public
	 * @param   string  $key
	 * @return  string
	 */
	static public function getHeader($key)
	{

		// Try to get it from the $_SERVER array first
		$temp = 'HTTP_' . strtoupper(str_replace('-', '_', $key));

		if (isset($_SERVER[$temp]))
		{
			return $_SERVER[$temp];
		}

		if (function_exists('apache_request_headers'))
		{
			$headers = apache_request_headers();
			if (isset($headers[$key]))
			{
				return $headers[$key];
			}
			$header = strtolower($key);
			foreach ($headers as $k => $v)
			{
				if (strtolower($k) == $key)
				{
					return $value;
				}
			}
		}

		return NULL;
	}

	/**
	 * @return string
	 */
	static public function getScheme()
	{
		return self::getServer('HTTPS') == 'on' ? 'https' : 'http';
	}

	/**
	 * Get the HTTP host.
	 *
	 * "Host" ":" host [ ":" port ] ; Section 3.2.2
	 * Note the HTTP Host header is not the same as the URI host.
	 * It includes the port while the URI host doesn't.
	 * <code>
	 * Nuo::getHttpHost()
	 * // return http(s)://yourdomain/base/path/
	 * <code>
	 * @static
	 * @access public
	 * @return string
	 */
	static public function getHttpHost()
	{
		$host = self::getServer('HTTP_HOST');

		if (!empty($host))
		{
			return $host;
		}

		$scheme = self::getScheme();
		$name = self::getServer('SERVER_NAME');
		$port = self::getServer('SERVER_PORT');

		if (NULL === $name)
		{
			return '';
		}
		elseif (($scheme == 'http' && $port == 80) || ($scheme == 'https' && $port == 443))
		{
			return $name;
		}
		else
		{
			return $name . ':' . $port;
		}
	}

	/**
	 * Get Requested Client Ip address
	 * @static
	 * @access  public
	 * @return  string
	 */
	static public function getClientIp()
	{
		if (self::getServer('HTTP_CLIENT_IP'))
		{
			self::getServer('HTTP_CLIENT_IP');
		}

		if (self::getServer('HTTP_X_FORWARDED_FOR'))
		{
			return self::getServer('HTTP_X_FORWARDED_FOR');
		}

		return self::getServer('REMOTE_ADDR', '127.0.0.1');
	}

	/**
	 * Is XMLHTTP request
	 * @static
	 * @access  public
	 * @return  boolean
	 */
	static public function isAsync()
	{
		return (self::getHeader('X_REQUESTED_WITH') == 'XMLHttpRequest');
	}

}
